//http 服务器
import * as http from "http"
import * as ws from "ws"

import { FormatInfo } from "../format/formatmgr";
import * as P from "path"
import * as fs from "fs";
import { TTPathTool } from "../format/pathtool";
import { GetFilesSync } from "../fstool/fstool";
export class Server {
    private port: number = 0;
    private info: FormatInfo | null = null
    private edit_filename: string = "";
    private edit_folder: string = "";
    private rootpath: string;
    private debug: boolean = false;
    GetPort(): number {
        return this.port;
    }

    Start(info: FormatInfo, filename: string, debug: boolean): void {
        this.info = info;
        this.debug = debug;
        if (debug) {
            console.warn("Debug 模式打开，这个模式不会主动关闭。");
        }
        this.rootpath = P.resolve(info.path_html);
        console.log("rootpath=" + this.rootpath);
        this.edit_filename = P.resolve(filename);
        this.edit_folder = TTPathTool.GetPathName(this.edit_filename);
        let server = http.createServer();

        this.port = 8000;
        server.addListener("request", this.OnHttp.bind(this))

        //外挂一个websocket 服务
        let wss = new ws.Server({ "server": server, "path": "/ws" });
        wss.on("connection", (socket) => {
            this.OnWebSocketConn(socket);
            socket.on("message", (data, binary) => {
                let _data = data as ws.Data
                this.OnWebSocketRecv(socket, _data as string)
            });
            socket.on("close", () => {
                this.OnWebSocketClose(socket)
            });

        });


        let lsucc = false;
        for (var i = 0; i < 1000; i++) {
            try {
                server.listen(this.port);
                console.log("http server start at 127.0.0.1:" + this.port);
                lsucc = true;
                break;
            }
            catch (e) {
                console.error(e);
            }
            this.port++;
        }
        if (!lsucc) {
            throw new Error("open http error.");
        }

    }
    private _wscount: number = 0;
    private _state: any = {};
    OnWebSocketConn(ws: ws.WebSocket) {
        this._wscount++;
        console.warn("ws conn:" + this._wscount);
        let json = { "cmd": "state", "data": this._state };
        ws.send(JSON.stringify(json));
    }
    OnWebSocketRecv(ws: ws.WebSocket, msg: string) {
        let cmd = "";
        try {

            console.warn("ws recv:" + msg);
            let json = JSON.parse(msg);
            cmd = json["cmd"] as string;
            if (cmd == "state") {
                this._state = json["data"];
                console.warn("ws setstate:" + this._state);
                return;
            }
            else if (cmd == "fs_find") {

                let path = json["data"]["path"] as string;
                let depth = json["data"]["depth"] as number;
                if (depth == undefined)
                    depth = 3;
                let files = GetFilesSync(this.edit_folder, path, depth);

                let outjson = { cmd: "filelist", data: files, "path": path }
                ws.send(JSON.stringify(outjson));
                return;
            }
            else if (cmd == "fs_load_text") {
                let path = json["data"] as string;
                let fullpath = "";
                if (!P.isAbsolute(path)) {
                    fullpath = P.join(this.edit_folder, path);
                }
                else {
                    fullpath = path;
                }
                let txt = fs.readFileSync(fullpath, { encoding: "utf-8" });
                let outjson = { cmd: "filedata", data: txt, "filename": path, "format": "text" }
                ws.send(JSON.stringify(outjson));
                return;
            }
            else if (cmd == "fs_load_binary") {

                let path = json["data"] as string;
                let fullpath = "";
                if (!P.isAbsolute(path)) {
                    fullpath = P.join(this.edit_folder, path);
                }
                else {
                    fullpath = path;
                }

                let bin = fs.readFileSync(fullpath);
                let outjson = { cmd: "filedata", data: bin.toString("hex"), "filename": path, "format": "binary" }
                ws.send(JSON.stringify(outjson));
                return;
            }
            else if (cmd == "fs_save_text") {
                let data = json["data"]["data"] as string;
                let filename = json["data"]["filename"] as string;
                let fullpath = "";
                if (!P.isAbsolute(filename)) {
                    fullpath = P.join(this.edit_folder, filename);
                }
                else {
                    fullpath = filename;
                }
                //filename = P.join(this.edit_folder, filename);
                //fs.unlinkSync(filename);
                fs.writeFileSync(fullpath, data);
                return;
            }
            else if (cmd == "fs_save_binary") {
                let hexstr = json["data"]["data"] as string;
                let filename = json["data"]["filename"] as string;
                let data = Buffer.from(hexstr, "hex");

                let fullpath = "";
                if (!P.isAbsolute(filename)) {
                    fullpath = P.join(this.edit_folder, filename);
                }
                else {
                    fullpath = filename;
                }
                //fs.unlinkSync(filename);
                fs.writeFileSync(fullpath, data);
                return;
            }
            else {
                console.log("unknown recv.");
            }
        }
        catch (e) {
            let outjson = { cmd: "onerror", data: "error", srccmd: cmd, "error": e }
            ws.send(JSON.stringify(outjson));
            console.log("ws error:" + e);
        }
    }
    static Hex2Str(data: Uint8Array): string {
        let str = "";
        for (var i = 0; i < data.length; i++) {
            let h = data[i].toString(16);
            if (h.length == 1) {
                str += ("0" + h);
            }
            else {
                str += h
            }
        }
        return str;
    }
    OnWebSocketClose(ws: ws.WebSocket) {
        this._wscount--;
        console.warn("ws close:" + this._wscount);
        if (this._wscount == 0) {
            //lights tag
            //此处决定所有websocket 退出后，宿主进程是否退出
            if (this.debug)//debug模式不退出
                return;
            process.exit(0);
        }
    }
    OnHttp(req: http.IncomingMessage, res: http.ServerResponse): void {

        let path: string = "";
        if (req.url != undefined) {
            path = req.url;
        }
        else {
            res.writeHead(400, "error");
            res.write("unknown error.");
            res.end();
            return;
        }




        let query = "";
        let qi = path.indexOf("?");
        if (qi && qi > 0) {
            path = path.substring(0, qi);
            query = path.substring(qi);

        }


        console.log("http: path= " + path + " ,query= " + query);
        if (path == "/") {
            console.log("http root");
            let headers: { [id: string]: string } = {};
            headers["Content-Type"] = "text/html;charset=utf-8;";
            res.writeHead(200, "ok", headers);
            res.write(this.GetMainHtml());
            res.end();
            return;
        }
        if (path == "/_init_.js") {
            let headers: { [id: string]: string } = {};
            headers["Content-Type"] = "application/javascript;charset=utf-8;";
            res.writeHead(200, "ok", headers);
            res.write(this.GetInitJS());
            res.end();
            return;
        }

        let localf = P.join(this.rootpath, path);
        if (!fs.existsSync(localf)) {

            let headers: { [id: string]: string } = { "Content-Type": "text/plain;charset=utf-8;" };
            res.writeHead(400, "ok", headers);
            res.write("文件不存在" + localf);
            res.end();
            return;
        }
        else {
            console.log("http get:" + localf);
            let ext = P.extname(path);
            let headers: { [id: string]: string } = {};
            let istxtfile = false;
            //这里的MIME，可能需要扩展的
            if (ext == ".txt") {
                headers["Content-Type"] = "text/plain;charset=utf-8;";
                istxtfile = true;
            }
            else if (ext == ".js" || ext == ".mjs") {
                headers["Content-Type"] = "application/javascript;charset=utf-8;";
                istxtfile = true;
            }
            else if (ext == ".json") {
                headers["Content-Type"] = "application/json;charset=utf-8;";
                istxtfile = true;
            }
            else if (ext == ".png") {
                headers["Content-Type"] = "image/png";
                istxtfile = false;
            }
            else if (ext == ".jpg" || ext == ".jpeg") {
                headers["Content-Type"] = "image/jpeg";
                istxtfile = false;
            }
            else if (ext == ".gif") {
                headers["Content-Type"] = "image/gif";
                istxtfile = false;
            }
            else {
                headers["Content-Type"] = "application/octet-stream";
            }
            res.writeHead(200, "ok", headers);
            if (istxtfile) {
                let txt = fs.readFileSync(localf, { encoding: "utf-8" });
                res.write(txt, "utf-8");
            }
            else {
                let bin = fs.readFileSync(localf);
                res.write(bin);
            }
            res.end();
            return;
        }


    }

    GetMainHtml(): string {
        let commonjs = "http://127.0.0.1:" + this.port + "/common.js";
        let mainjs = "http://127.0.0.1:" + this.port + "/" + this.info.initscript;
        return `<html>
        <head>
        <title>`+ this.edit_filename + `</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0"/>
        <script src="_init_.js"></script>
        <script type="module" src="`+ mainjs + `"></script>
        </head>
        <body>
        </body>
        </html>
        `;
    }
    GetInitJS(): string {
        let header = `"use strict";
let plugin_path = "`+ "http://127.0.0.1:" + this.port + "/" + `";
let edit_filename = "`+ TTPathTool.GetFileName(this.edit_filename) + `";
let ws_addr = "`+ "ws://127.0.0.1:" + this.port + "/ws" + `";
`
        let js = `
//下面是固定代码
function getVsCodeEditFileName() {
    return edit_filename;
}
function getVsCodePluginPath() {
    return plugin_path;
}
class Api_Impl {
    constructor(ws_addr) {
        this._ws = new WebSocket(ws_addr, "ws");
        this._ws.onopen = (e) => {
            console.warn("websocket in.");
        };
        this._ws.onerror = (e => {
            console.warn("websocket error");
        });
        this._ws.onclose = (e) => {
            console.warn("websocket colse.");
            window.close();
        };
        this._ws.onmessage = (e) => {
            //console.log("websocket recv:" + e.data.length);
            let msg = JSON.parse(e.data);
            let cmd = msg["cmd"];
            if (cmd == "state") {
                this._state = msg["data"];
                console.log("recv update state.");
                return;
            }
            
           
            if (window.onmessage != undefined) {
                let data = { "data": msg};
                window.onmessage(data);
            }
        };
    }
    postMessage(msg) {
        this._ws.send(JSON.stringify(msg));
    }
    getState() {
        return this._state;
    }
    setState(state) {
        this._state = state;
        let send = { "cmd": "state", "data": state };
        this._ws.send(JSON.stringify(send));
    }
}
let __hide_api = new Api_Impl(ws_addr);
function acquireVsCodeApi() {
    return __hide_api;
}

        `;
        return header + js;
    }


}